﻿namespace MefContrib.Models.Provider
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using MefContrib.Models.Provider.Validation;
    
    /// <summary>
    /// A static cache for maintaining records of all known <see cref="DefinitionProviderPartCatalog"/> objects
    /// and their <see cref="ProviderComposablePartDefinition"/> objects.
    /// </summary>
    public class DefinitionLocator
    {
        /// <summary>
        /// Initializes static members of the <see cref="DefinitionLocator"/> class.
        /// </summary>
        static DefinitionLocator()
        {
            Catalogs = new Collection<DefinitionProviderPartCatalog>();
        }

        /// <summary>
        /// Prevents a default instance of the <see cref="DefinitionLocator"/> class from being created.
        /// </summary>
        private DefinitionLocator()
        {
        }

        /// <summary>
        /// Gets a list of all known catalogs and their definitions.
        /// </summary>
        /// <value>A <see cref="Dictionary{TKey,TValue}"/> object.</value>
        public static Dictionary<DefinitionProviderPartCatalog, List<ProviderComposablePartDefinition>> Definitions { get; private set; }

        /// <summary>
        /// Gets a definition for a specified object instance.
        /// </summary>
        /// <param name="instance">The object to get the definition for.</param>
        /// <returns>A <see cref="ProviderComposablePartDefinition"/> object if a definition could be found; otherwise <see langword="null" />.</returns>
        /// <exception cref="ArgumentNullException">The value specified for the <paramref name="instance"/> parameter was <see langword="null" />.</exception>
        public static ProviderComposablePartDefinition GetDefinition(object instance)
        {
            instance
                .Require("instance")
                .NotNull();

            var matches =
                from catalog in Catalogs
                let definition = catalog.GetDefinition(instance)
                where definition != null
                select definition;

            return matches.FirstOrDefault();
        }

        /// <summary>
        /// Gets a list of all known <see cref="DefinitionProviderPartCatalog"/> objects.
        /// </summary>
        /// <value>A <see cref="Collection{T}"/> object.</value>
        protected static Collection<DefinitionProviderPartCatalog> Catalogs { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="catalog"></param>
        /// <exception cref="ArgumentNullException">The provided value for the <paramref name="catalog"/> parameter was <see langword="null"/>.</exception>
        public static void Register(DefinitionProviderPartCatalog catalog)
        {
            catalog
                .Require("catalog")
                .NotNull();

            if (!Catalogs.Contains(catalog))
            {
                Catalogs.Add(catalog);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="catalog"></param>
        /// <exception cref="ArgumentNullException">The provided value for the <paramref name="catalog"/> parameter was <see langword="null"/>.</exception>
        public static void UnRegister(DefinitionProviderPartCatalog catalog)
        {
            catalog
                .Require("catalog")
                .NotNull();

            if (Catalogs.Contains(catalog))
            {
                Catalogs.Remove(catalog);
            }
        }
    }
}